package com.fuyao.cloud.admin.flow.controller;

import com.fuyao.cloud.admin.flow.dto.HighLightedNodeDTO;
import com.fuyao.cloud.admin.flow.util.FlowableHelper;
import com.fuyao.cloud.common.core.util.R;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.constants.BpmnXMLConstants;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.SequenceFlow;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.TaskInfo;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Slf4j
@RestController
@RequiredArgsConstructor
@Api(value = "flowable", tags = "流向信息获取")
@RequestMapping("/flowable/line")
public class FlowableModelHighLightController {
    private final RuntimeService runtimeService;
    private final RepositoryService repositoryService;
    private final HistoryService historyService;
    private static final BpmnXMLConverter bpmnXMLConverter = new BpmnXMLConverter();

    @ApiOperation(value = "获取已走过的流程元素和当前流程元素")
    @ApiImplicitParam(name = "businessKey", value = "businessKey", required = true, dataTypeClass = String.class, paramType = "path")
    @GetMapping(value = "/getHighLightedNode/{businessKey}")
    public R<HighLightedNodeDTO> getHighLightedNode(@PathVariable("businessKey") String businessKey) {
        return R.ok(getHighLightedNodeVo(businessKey));
    }

    private HighLightedNodeDTO getHighLightedNodeVo(String businessKey) {
        HighLightedNodeDTO highLightedNodeVo = new HighLightedNodeDTO();
        String processInstanceId;
        String processDefinitionId;

        ProcessInstance processInstance = runtimeService
                .createProcessInstanceQuery()
                .processInstanceBusinessKey(businessKey)
                .singleResult();

        if (processInstance == null) {
            HistoricProcessInstance historicProcessInstance = historyService
                    .createHistoricProcessInstanceQuery()
                    .processInstanceBusinessKey(businessKey)
                    .singleResult();
            processInstanceId = historicProcessInstance.getId();
            processDefinitionId = historicProcessInstance.getProcessDefinitionId();
            highLightedNodeVo.setModelName(historicProcessInstance.getName());
            highLightedNodeVo.setActiveActivityIds(Collections.emptySet());
        } else {
            processInstanceId = processInstance.getProcessInstanceId();
            processDefinitionId = processInstance.getProcessDefinitionId();
            highLightedNodeVo.setModelName(processInstance.getName());
            highLightedNodeVo.setActiveActivityIds(new HashSet<>(runtimeService.getActiveActivityIds(processInstanceId)));
        }
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);

        List<HistoricTaskInstance> allHistoricTaskInstances = FlowableHelper.getAllHistoricTaskInstances(historyService, businessKey);
        List<String> nodeIds = allHistoricTaskInstances.stream().map(TaskInfo::getTaskDefinitionKey).collect(Collectors.toList());

        List<HistoricActivityInstance> finishedHistoricActivityInstances = historyService
                .createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId)
                .finished()
                .list();

        Set<String> dirty = finishedHistoricActivityInstances.stream()
                .filter(historicActivityInstance -> BpmnXMLConstants.ELEMENT_TASK_USER.equals(historicActivityInstance.getActivityType()))
                .map(HistoricActivityInstance::getActivityId)
                .filter(elementId -> !nodeIds.contains(elementId))
                .collect(Collectors.toSet());

        List<FlowElement> flowElements = FlowableHelper.getFlowElements(bpmnModel, dirty);

        Set<String> elementIds = flowElements.stream()
                .flatMap(flowElement -> {
                    Set<String> ids = new HashSet<>();
                    ids.add(flowElement.getId());
                    Set<SequenceFlow> outgoing = FlowableHelper.getOutgoing(flowElement);
                    Set<SequenceFlow> incoming = FlowableHelper.getIncoming(flowElement);
                    outgoing.addAll(incoming);
                    for (SequenceFlow sequenceFlow : outgoing) {
                        ids.add(sequenceFlow.getId());
                    }
                    return ids.stream();
                })
                .collect(Collectors.toSet());

        highLightedNodeVo.setHighLightedFlows(finishedHistoricActivityInstances
                .stream()
                .map(HistoricActivityInstance::getActivityId)
                .filter(s -> !elementIds.contains(s))
                .collect(Collectors.toSet())
        );

        highLightedNodeVo.setModelXml(new String(bpmnXMLConverter.convertToXML(bpmnModel), StandardCharsets.UTF_8));

        return highLightedNodeVo;
    }
}
